


#include "qe_device.h"
#include "qelib.h"
#include "FreeRTOS.h"



#define PSU_UART0_BASE 0xFF000000
#define PSU_UART1_BASE 0xFF010000
#define PSU_UART_FREQ  99999000



/* Control Register */
#define PSU_UART_CR_STPBRK     0x00000100  /* Stop transmitter break */
#define PSU_UART_CR_STTBRK     0x00000080  /* Start transmitter break */
#define PSU_UART_CR_RSTTO      0x00000040  /* Restart receiver timeout counter */
#define PSU_UART_CR_TXDIS      0x00000020  /* Transmit disable */
#define PSU_UART_CR_TXEN       0x00000010  /* Transmit enable */
#define PSU_UART_CR_RXDIS      0x00000008  /* Receive disable */
#define PSU_UART_CR_RXEN       0x00000004  /* Receive enable */
#define PSU_UART_CR_TXRES      0x00000002  /* Software reset for Tx data path */
#define PSU_UART_CR_RXRES      0x00000001  /* Software reset for Rx data path */

/* Mode Register */
#define PSU_UART_MR_WSIZE_BYTE 0x00001000  /* One byte is always written or read from FIFOs */
#define PSU_UART_MR_CH_ECHO    0x00000100  /* Channel model automatic echo */
#define PSU_UART_MR_CH_LO_LOOP 0x00000200  /* Channel model local loopback */
#define PSU_UART_MR_CH_RE_LOOP 0x00000300  /* Channel model remote loopback */
#define PSU_UART_MR_STOPB_1    0x00000000  /* Stop bit 1 */
#define PSU_UART_MR_STOPB_1_5  0x00000040  /* Stop bit 1.5 */
#define PSU_UART_MR_STOPB_2    0x00000080  /* Stop bit 2 */
#define PSU_UART_MR_PAR_EVE    0x00000000  /* Even parity */
#define PSU_UART_MR_PAR_ODD    0x00000008  /* Odd parity */
#define PSU_UART_MR_PAR_NO     0x00000020  /* No parity */
#define PSU_UART_MR_8_BIT      0x00000000  /* Character length 8 bits */
#define PSU_UART_MR_7_BIT      0x00000004  /* Character length 7 bits */
#define PSU_UART_MR_6_BIT      0x00000006  /* Character length 6 bits */

/* Interrupt Enable Register */
#define PSU_UART_IER_RBRK      QE_BIT(13)  /* Receiver break detect interrupt */
#define PSU_UART_IER_TOVR      QE_BIT(12)  /* Transmitter FIFO Overflow interrupt */
#define PSU_UART_IER_TNFUL     QE_BIT(11)  /* Transmitter FIFO Nearly Full interrupt */
#define PSU_UART_IER_TTRIG     QE_BIT(10)  /* Transmitter FIFO Trigger interrupt */
#define PSU_UART_IER_DMSI      QE_BIT(9)  /* Delta Modem Status Indicator interrupt */
#define PSU_UART_IER_RTMOE     QE_BIT(8)  /* Receiver Timeout Error interrupt */
#define PSU_UART_IER_RPARE     QE_BIT(7)  /* Receiver Parity Error interrupt */
#define PSU_UART_IER_RFRME     QE_BIT(6)  /* Receiver Framing Error interrupt */
#define PSU_UART_IER_ROVRE     QE_BIT(5)  /* Receiver Overflow Error interrupt */
#define PSU_UART_IER_TFUL      QE_BIT(4)  /* Transmitter FIFO Full interrupt */
#define PSU_UART_IER_TEMPTY    QE_BIT(3)  /* Transmitter FIFO Empty interrupt */
#define PSU_UART_IER_RFUL      QE_BIT(2)  /* Receiver FIFO Full interrupt */
#define PSU_UART_IER_REMPTY    QE_BIT(1)  /* Receiver FIFO Empty interrupt */
#define PSU_UART_IER_RTRIG     QE_BIT(0)  /* Receiver FIFO Trigger interrupt: */

#define PSU_UART_SR_TXACTIVE   QE_BIT(11)     /* TX active */
#define PSU_UART_SR_TXFULL     QE_BIT(4)      /* TX FIFO full */
#define PSU_UART_SR_RXEMPTY    QE_BIT(1)      /* RX FIFO empty */

#define UART_LOGDOMAIN          "psu-uart"
#define uart_debug(...)  	    qelog_debug(UART_LOGDOMAIN, __VA_ARGS__)
#define uart_info(...)   	    qelog_info(UART_LOGDOMAIN, __VA_ARGS__)
#define uart_notice(...) 	    qelog_notice(UART_LOGDOMAIN, __VA_ARGS__)
#define uart_warning(...)       qelog_warning(UART_LOGDOMAIN, __VA_ARGS__)
#define uart_critical(...)      qelog_critical(UART_LOGDOMAIN, __VA_ARGS__)
#define uart_error(...)  	    qelog_error(UART_LOGDOMAIN, __VA_ARGS__)
#define uart_fatal(...)		    qelog_fatal(UART_LOGDOMAIN, __VA_ARGS__)

typedef struct {
    volatile qe_u32 cr;          /* 0x00 Control Register [8:0] */
    volatile qe_u32 mr;          /* 0x04 Mode Register */
    volatile qe_u32 ier;         /* 0x08 Interrupt Enable Register */
    volatile qe_u32 idr;         /* 0x0C Interrupt Disable Register */
    volatile qe_u32 imr;         /* 0x10 Interrupt Mask Register */
    volatile qe_u32 isr;         /* 0x14 Interrupt Status Register */
    volatile qe_u32 brg;         /* 0x18 Baud Rate Generator [15:0] */
    volatile qe_u32 rtm;         /* 0x1C Receiver Timeout Register */
    volatile qe_u32 rftl;        /* 0x20 Receiver FIFO Trigger Level Register */
    volatile qe_u32 reserved2[2];
    volatile qe_u32 sr;          /* 0x2C Channel Status [11:0] */
    volatile qe_u32 fifo;        /* 0x30 FIFO [15:0] or [7:0] */
    volatile qe_u32 brd;         /* 0x34 Buad Rate Divider [7:0] */
} psu_uart_regs;

typedef struct {
    psu_uart_regs *regs;
    int irq;
    int clock;
    int timeout;
    int baudrate;
    int buffer_size;
    int rx_trigger_level;
    int rx_indicate_level;
    qe_bool is_transfer_started;
} psu_uart_dev;

static psu_uart_dev uart0 = {
    .regs  = (psu_uart_regs *)PSU_UART0_BASE,
    .irq   = 53,
    .clock = PSU_UART_FREQ,
    .timeout = 4,
    .baudrate = 115200,
    .buffer_size = 1024,
    .rx_trigger_level = 8,
    .rx_indicate_level = 32,
};

static psu_uart_dev uart1 = {
    .regs  = (psu_uart_regs *)PSU_UART1_BASE,
    .irq   = 54,
    .clock = PSU_UART_FREQ,
    .timeout = 4,
    .baudrate = 115200,
    .buffer_size = 2048,
    .rx_trigger_level = 16,
    .rx_indicate_level = 32,
};

static qe_serial_dev serial0;
static qe_serial_dev serial1;

static void psu_uart_irq_tx_enable(psu_uart_regs *regs, qe_bool en)
{
    qe_u32 mask;

    mask = PSU_UART_IER_TEMPTY;

    if (en) {
        regs->ier = mask;
    } else {
        regs->idr = mask;
    }
}

static void psu_uart_irq_rx_enable(psu_uart_regs *regs, qe_bool en)
{
    qe_u32 mask;

    mask = PSU_UART_IER_RTMOE |
           PSU_UART_IER_RTRIG;

    if (en) {
        regs->ier = mask;
    } else {
        regs->idr = mask;
    }
}

static void psu_uart_tx_rx_enable(psu_uart_regs *regs, qe_bool en)
{
    if (en) {
        regs->cr |= PSU_UART_CR_TXEN | PSU_UART_CR_RXEN;
    } else {
        regs->cr &= ~(PSU_UART_CR_TXEN | PSU_UART_CR_RXEN);
    }
    //regs->cr |= ZYNQ_UART_CR_RSTTO;
}

static qe_unused void psu_uart_fifo_reset(psu_uart_regs *regs)
{
    regs->cr |= PSU_UART_CR_TXRES | PSU_UART_CR_RXRES;
}

static void psu_uart_set_rx_timeout(psu_uart_dev *uart, qe_u32 timeout)
{
    uart->regs->rtm = timeout & 0x000000FF;
    uart->regs->cr |= PSU_UART_CR_RSTTO;
}

static qe_ret psu_uart_setbrg(psu_uart_dev *uart, qe_u32 baudrate)
{
    qe_u32 calc_bauderror, bdiv, bgen;
    qe_u32 calc_baud = 0;

    if ((baudrate * 2) > uart->clock) {
        uart_error("baudrate %d error", baudrate);
        return qe_err_param;
    }

	/*                master clock
	 * Baud rate = ------------------
	 *              bgen * (bdiv + 1)
	 *
	 * Find acceptable values for baud generation.
	 */
    for (bdiv=4; bdiv<255; bdiv++) {

        /* Calculate the value for BRGR register */
        bgen = uart->clock / (baudrate * (bdiv + 1));

        /* check if bgen invaild */
        if (bgen < 2 || bgen > 65535) 
            continue;
        
        /* Calculate the baud rate from the BRGR value */
        calc_baud = uart->clock / (bgen * (bdiv + 1));

		/*
		 * Use first calculated baudrate with
		 * an acceptable (<3%) error
		 */
        if (baudrate > calc_baud)
            calc_bauderror = baudrate - calc_baud;
        else
            calc_bauderror = calc_baud - baudrate;
        if (((calc_bauderror * 100) / baudrate) < 3)
            break;
    }

    /* disable uart */
    psu_uart_tx_rx_enable(uart->regs, qe_false);
    
    uart->regs->brd = bdiv;
    uart->regs->brg = bgen;

    /* enable uart */
    psu_uart_tx_rx_enable(uart->regs, qe_true);

    return qe_ok;
}

static void psu_uart_probe(psu_uart_dev *uart)
{
    uart->regs->rftl = uart->rx_trigger_level;

    psu_uart_set_rx_timeout(uart, uart->timeout);

    /* enable rx/tx */
    psu_uart_tx_rx_enable(uart->regs, qe_true);
}

static qe_ret psu_uart_configure(qe_serial_dev *serial,
    qe_serial_configure *cfg)
{
    qe_u32 mr = 0x0;
    psu_uart_dev *uart = (psu_uart_dev *)serial->parent.private;
    psu_uart_regs *regs = uart->regs;

    /* disable interrrupt */
    psu_uart_irq_rx_enable(uart->regs, qe_false);

    /* stop bits */
    if (cfg->stop_bits == STOP_BITS_1) {
        mr |= PSU_UART_MR_STOPB_1;
    } else if (cfg->stop_bits == STOP_BITS_2) {
        mr |= PSU_UART_MR_STOPB_2;
    } else if (cfg->stop_bits != STOP_BITS_1) {
        uart_error("stop bit %d not support", cfg->stop_bits);
        return qe_err_notsupport;
    }

    /* parity */
    if (cfg->parity == PARITY_EVEN) {
        mr |= PSU_UART_MR_PAR_EVE;
    } else if (cfg->parity == PARITY_ODD) {
        mr |= PSU_UART_MR_PAR_ODD;
    } else if (cfg->parity == PARITY_NONE) {
        mr |= PSU_UART_MR_PAR_NO;
    } else {
        uart_error("parity:%d not support", cfg->parity);
        return qe_err_notsupport;
    }

    /* data bits */
    if (cfg->data_bits == DATA_BITS_8) {
        mr |= PSU_UART_MR_8_BIT;
    } else if (cfg->data_bits == DATA_BITS_7) {
        mr |= PSU_UART_MR_7_BIT;
    } else if (cfg->data_bits == DATA_BITS_6) {
        mr |= PSU_UART_MR_6_BIT;
    } else {
        uart_error("data bit:%d not support", cfg->data_bits);
        return qe_err_notsupport;
    }

    regs->mr = mr;

    /* baud rate */
    return psu_uart_setbrg(uart, cfg->baud_rate);
}

static void psu_uart_xfer(qe_serial_dev *serial, psu_uart_dev *dev)
{
    int i;
    char fifo[64];
    qe_uint wait;
    qe_uint read_once;
    wait = qe_ringbuffer_wait(serial->tx_ring);
    read_once = qe_min(wait, 64);
    if (!read_once)
        return;
    qe_ringbuffer_read(serial->tx_ring, fifo, read_once);
    for (i=0; i<read_once; i++) {
    	dev->regs->fifo = fifo[i];
    }
}

static qe_int psu_uart_transfer_start(qe_serial_dev *serial)
{
    psu_uart_dev *dev;

    qe_assert(serial != QE_NULL);
    
    dev = serial->parent.private;

    if (dev->is_transfer_started) {
        uart_warning("transfer already started");
        return -(qe_err_common);
    }
    dev->is_transfer_started = qe_true;
    //psu_uart_irq_rx_enable(dev->regs, qe_false);
    psu_uart_irq_tx_enable(dev->regs, qe_true);
    /* do one xfer */
    psu_uart_xfer(serial, dev);

    return 0;
}

static void psu_uart_tx_isr(qe_serial_dev *serial, 
    psu_uart_dev *dev)
{
    qe_int wait = 0;

    if (!dev->is_transfer_started)
        return;

    wait = qe_ringbuffer_wait(serial->tx_ring);
    if (!wait) {
        dev->is_transfer_started = qe_false;
        psu_uart_irq_tx_enable(dev->regs, qe_false);
        //psu_uart_irq_rx_enable(dev->regs, qe_true);
        if (serial->parent.tx_complete)
            serial->parent.tx_complete(&serial->parent, QE_NULL);
    } else {
        psu_uart_xfer(serial, dev);
    }
}

static void psu_uart_rx_isr(qe_serial_dev *serial, 
    psu_uart_dev *uart, qe_u32 isr)
{
    char data;
    qe_uint wait;
    psu_uart_regs *regs = uart->regs;

    while (!(regs->sr & PSU_UART_SR_RXEMPTY)) {
        data = regs->fifo;
        qe_ringbuffer_write(serial->rx_ring, &data, 1);
    }

    wait = qe_ringbuffer_wait(serial->rx_ring);

    if ((isr & PSU_UART_IER_RTMOE) || wait > uart->rx_indicate_level) {
        if (isr & PSU_UART_IER_RTMOE && wait) {
            regs->cr |= PSU_UART_CR_RSTTO;
        }
        if (serial->parent.rx_indicate) {
            uart_debug("wait %d", wait);
            serial->parent.rx_indicate(&serial->parent, wait);
        }
    }
}

static void psu_uart_isr(void *ref)
{
    volatile qe_u32 isr;

    qe_serial_dev *serial = (qe_serial_dev *)ref;
    psu_uart_dev *uart = (psu_uart_dev *)serial->parent.private;

    isr = uart->regs->imr;
    isr &= uart->regs->isr;
    //uart_info("isr 0x%x 0x%x", isr, uart->regs->imr);
    if (isr & (PSU_UART_IER_RTRIG | PSU_UART_IER_RTMOE)) {
        psu_uart_rx_isr(serial, uart, isr);
    }

    if (isr & PSU_UART_IER_TEMPTY) {
        psu_uart_tx_isr(serial, uart);
    }

    /* clear interrupt status */
    uart->regs->isr = isr;
}

static qe_ret psu_uart_ioctl(qe_serial_dev *serial, int cmd, void *arg)
{
    psu_uart_dev *uart = serial->parent.private;

    qe_assert(serial != QE_NULL);
    qe_assert(uart != QE_NULL);

    switch (cmd) {
    
    case QE_DEV_IOC_CLR_INT:
        uart_debug("disable irq:%d", uart->irq);
        psu_uart_irq_rx_enable(uart->regs, qe_false);
        vPortDisableInterrupt(uart->irq);
        break;

    case QE_DEV_IOC_SET_INT:
        uart_debug("enable irq:%d", uart->irq);
        /* register freertos interrupt */
        vPortEnableInterrupt(uart->irq);
        psu_uart_irq_rx_enable(uart->regs, qe_true);
        break;
    }

    return qe_ok;
}

static int psu_uart_getc(qe_serial_dev *serial)
{
    int c;
    psu_uart_dev *uart = serial->parent.private;
    psu_uart_regs *regs = uart->regs;

    qe_assert(serial != QE_NULL);
    qe_assert(uart != QE_NULL);

    c = -1;

    if (!(regs->sr & PSU_UART_SR_RXEMPTY))
        c = regs->fifo;

    return c;
}

static int psu_uart_putc(qe_serial_dev *serial, char c)
{
    psu_uart_dev *uart = serial->parent.private;
    psu_uart_regs *regs = uart->regs;

    qe_assert(serial != QE_NULL);
    qe_assert(uart != QE_NULL);

    while (regs->sr & PSU_UART_SR_TXFULL)
        ;
    
    regs->fifo = c;
    //uart_info("put 0x%x", c);

    return 1;
}

static qe_uart_ops psu_uart_ops = {
    .configure      = psu_uart_configure,
    .ioctl          = psu_uart_ioctl,
    .getc           = psu_uart_getc,
    .putc           = psu_uart_putc,
    .transfer_start = psu_uart_transfer_start,
};

static void serial_register(psu_uart_dev *dev, qe_const_str name, 
    qe_serial_dev *serial)
{
    qe_serial_configure config = QE_SERIAL_DEFCONFIG;

    config.bufsz     = dev->buffer_size;
    config.baud_rate = dev->baudrate;

    psu_uart_probe(&uart1);

    xPortInstallInterruptHandler(dev->irq, psu_uart_isr, serial);

    serial->config = config;
    serial->ops = &psu_uart_ops;
    qe_serial_register(serial, name,
        QE_DEV_F_RDWR|QE_DEV_F_INT_RX|QE_DEV_F_INT_TX, &uart1);

    uart_debug("%s register", name);
}

static qe_init int psu_uart_init(void)
{
    qelog_domain_set_level(UART_LOGDOMAIN, QELOG_INFO);
    serial_register(&uart0, "uart0", &serial0);
    serial_register(&uart1, "uart1", &serial1);
    return 0;
}
QE_BOARD_EXPORT(psu_uart_init);
